############################################################################
#
# ldap.pm
#
# General LDAP support routines
#
# Copyright (C) 2006 The University of Chicago
#
# Darren Young <darren.young@gsb.uchicago.edu>
#
# $Id: ldap.pm,v 1.21 2009/08/12 18:50:51 dyoung2 Exp $
#
############################################################################
#
# NOTICE
# ------
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
# USA.
#
############################################################################
#
# CHANGELOG
# ---------
# 
# $Log: ldap.pm,v $
# Revision 1.21  2009/08/12 18:50:51  dyoung2
#   * Added change to remove mail attributes for guest faculty
#
# Revision 1.20  2009/07/23 16:54:47  dyoung2
#   * Set alumni to be on Exchange
#
# Revision 1.19  2009/01/29 17:07:06  dyoung2
#   * Booth name change
#
# Revision 1.18  2008/06/30 16:30:07  dyoung2
#   * Added AD Group population
#
# Revision 1.17  2008/06/18 21:10:56  dyoung2
#   * Version for testing with new LDAP code.
#
# Revision 1.16  2008/04/22 20:31:24  dyoung2
#   * Cleaned up last name more
#
# Revision 1.15  2008/04/10 22:52:16  dyoung2
#   * Added the rest of the POSIX attribute adds.
#
# Revision 1.14  2008/04/04 21:53:16  dyoung2
#   * Changes for registrar in testing.
#
# Revision 1.13  2007/08/02 19:46:03  dyoung2
#   * Set students to 'mailAllowedServiceAccess' => '+ALL:ALL' while not on Exchange.
#
# Revision 1.12  2007/08/02 19:16:56  dyoung2
#   * Added staff first.last email addresses.
#   * Added exchange mail forwarding option
#
# Revision 1.11  2006/08/03 17:07:12  dyoung2
#   * Added routines to remove user accounts from ldap
#
# Revision 1.10  2006/07/21 20:50:20  dyoung2
#   * Added _deleteIMSLdapAccount
#
# Revision 1.9  2006/07/14 23:28:06  dyoung2
#   * Added mailQuota and gsbaccounttype to the _addLdapAccount
#
# Revision 1.8  2006/07/13 21:37:21  dyoung2
#   * First *seemingly* working add.
#
# Revision 1.7  2006/07/12 18:37:15  dyoung2
#   * Added all the password get/set functions
#
# Revision 1.6  2006/07/10 02:34:47  dyoung2
#   * Updated from new idstools ldap module
#
# Revision 1.8  2006/07/06 06:32:25  root
#   * Changed successful ops on _addOrChangeLdapAttrVal to be debug()
#
# Revision 1.7  2006/07/06 03:36:13  root
#   * Switched to use logging module.
#
# Revision 1.6  2006/06/13 17:21:59  root
#   * Changed _ldapConnect to get the connect host from an argument
#
# Revision 1.5  2006/06/08 00:59:56  root
#   * Added _setRoutedUser.
#
# Revision 1.4  2006/06/07 22:40:32  root
#   * Fixed problem in modify where it was trying to add.
#
# Revision 1.3  2006/06/07 22:00:09  root
#   * Added _delete function.
#
# Revision 1.2  2006/06/07 19:10:33  root
#   * Added header
#
# Revision 1.1  2006/06/07 19:05:35  root
#   * Initial version.
#
############################################################################
#
# NOTES:
# ------
#
############################################################################

my $cvsid    = '$Id: ldap.pm,v 1.21 2009/08/12 18:50:51 dyoung2 Exp $';
my @cvsinfo  = split( ' ', $cvsid );
my $NAME    = File::Basename::basename($0);
my $VERSION = $cvsinfo[2];


############################################################################
#                               B E G I N
############################################################################

BEGIN {

    # Pragmas
    use strict;

    # "Standard" modules we use
    use FindBin;
    use Config::Simple;
    use lib "$FindBin::Bin/../lib";

    # Standard account maint modules to use
    use logging;
    use errcodes;
    use utils;
    
    # Modules that this module uses
    use amtools;
    use ldap;
    use ds;
    use Net::LDAP;
    use Lingua::EN::NameParse;

}


# ------------------------------------------------------------------------------ 
#                             V A R I A B L E S
# ------------------------------------------------------------------------------ 
our $LOGFILE    = "$FindBin::Bin/../log/acctmgr.log" unless($LOGFILE); 
our $CONFIGFILE = "$FindBin::Bin/../etc/acctmgr.cfg" unless($CONFIGFILE); 


# ------------------------------------------------------------------------------
#                             C O N F I G   F I L E
# ------------------------------------------------------------------------------
our $CFG = new Config::Simple( filename => $CONFIGFILE ) or die "$!\n" unless($CFG);


###############################################################################
# NAME        : _ldapConnect
# DESCRIPTION : Connect to an LDAP server
# ARGUMENTS   : None
# RETURN      : scalar(ref(ldap))
# NOTES       : None
###############################################################################
sub _ldapConnect {
    my ($sname) = @_; 
    my $name = "_ldapConnect()";
    my ( $package, $filename, $line ) = caller; 
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");      

    if ( ! defined($sname) ) {
        logmsg("$name: ERROR, you have to give me a host to connect to");
        exit();
    }

    my @adminfo = get_ds_account("$FindBin::Bin/../etc/admpw.conf", "manager");
    my $binddn  = $adminfo[0];
    my $bindpw  = $adminfo[1];

    my $host    = $CFG->param("ldap-$sname.host");
    my $port    = $CFG->param("ldap-$sname.port");
    my $timeout = $CFG->param("ldap-$sname.timeout");
    my $ldapver = $CFG->param("ldap-$sname.ldapver");

    my $conn    = "$host" . ':' . "$port";
    my $ldap    = "";

    # attempt to connect to the LDAP service. if we connect, move on, otherwise
    # we have to return a fatal error condition
    # XXX - needs to be modified for multiple ldap servers
    if ( $ldap  = Net::LDAP->new($conn, => timeout=>$timeout, version=>$ldapver)) {
        debug("$name: established LDAP connection to $conn");
    } else {
        logmsg("$name: FAILED to estblish LDAP connection to $conn");
        _ldapStatus($ldap);
        return(0);
    }

    # bind to the LDAP server
    if ( $ldap->bind( $binddn, password => $bindpw ) ) {
        debug("$name: successful bind to $conn");
    } else {
        _ldapStatus($ldap);
        return(0);
    }

    debug("$name: returning with val $ldap");
    return($ldap);
}


###############################################################################
# NAME        : _ldapClose
# DESCRIPTION : Unbind and disconnect a previously connected LDAP reference
# ARGUMENTS   : scalar(ref(ldap))
# RETURN      : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _ldapClose {
    my ($ldapref) = @_;
    my $name = "_ldapClose()";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    if ( $ldapref->unbind ) {
        debug("$name: unbound from ldap server");
    } else {
        logmsg("$name: FAILED to unbind from ldap server");
        _ldapStatus($ldapref);
        return(0);
    }
    
    if ( $ldapref->disconnect ) {
        debug("$name: disconnected from ldap server");
    } else {
        logmsg("$name: FAILED to disconnect from ldap server");
        _ldapStatus($ldapref);
        return(0);
    }

    return(1);
}


###############################################################################
# NAME        : _getLdapUserAttrVal
# DESCRIPTION : Retrieve an LDAP attribute for a given user
# ARGUMENTS   : scalar(ref(ldap))   -> reference to an Net::LDAP connection
#             : scalar(valtype)     -> single or multi
#             : scalar(uid)         -> LDAP uid
#             : scalar(attribute)   -> LDAP attribute to return
# RETURN      : scalar(attribute) -or- array(attribute)
# NOTES       : 
###############################################################################
sub _getLdapUserAttrVal {
    my ($ldapref, $valtype, $uname, $attr) = @_;
    my $name = "_getLdapUserAttrVal()";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $singleval  = "";
    my @multival   = "";
    my $basedn     = "o=gsb,dc=uchicago,dc=edu";
    my $entry      = "";

    _checkLdapObjRef($ldapref) or die();

    # Start this search from the top level of the LDAP tree
    $mesg = $ldapref->search( base   => "$basedn",
                              filter => '(&(uid=' . $uname . '))',
                              attrs  => "$attr"
                             );

    if ( $mesg->code ) {
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count > 0 ) {
            if ( $mesg->count > 1 ) {
                logmsg( "$name: found more than 1 LDAP entry for $uname (that's bad)" );
                return(0);
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                $entry = $mesg->entry(0);

                # return the value based on the type (single or multi)
                if ( $entry->exists("$attr")) {
                    if ( $valtype eq "single" ) {
                        $singleval = $entry->get_value("$attr");
                        return($singleval);
                    } elsif ( $valtype eq "multi" ) {
                        @multival = $entry->get_value("$attr");
                        return(@multival);
                    } else {
                        logmsg("$name: invalid value type: $valtype");
                        return(0);
                    }
                } else {
                    logmsg("$name: attribute ($attr) for $uname is not set");
                    return(0);
                }
            }
        } else {
            logmsg("$name: FAILED to find an LDAP entry for $uname");
            return(0);
        }
    }
}


###############################################################################
# NAME        : _addOrModifyLdapUserAttrVal
# DESCRIPTION : Add/Modify an attribut/value for a given user
# ARGUMENTS   : scalar(ref(ldap))   -> Net::LDAP reference
#             : scalar(operation)   -> add or mod
#             : scalar(uid)         -> LDAP uid
#             : scalar(attribute)   -> LDAP attribute
#             : scalar(value)       -> value for attribut
# RETURN      : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _addOrModifyLdapUserAttrVal {
    my ($ldapref, $optype, $uname, $attr, $val) = @_;
    my $name = "_addOrModifyLdapUserAttrVal";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    _checkLdapObjRef($ldapref) or die();

    my $mesg;
    my $entry;
    my $basedn = "o=gsb,dc=uchicago,dc=edu";
    my $result;

    $mesg = $ldapref->search( base   => "$basedn",
                              filter => '(&(uid=' . $uname . '))',
                              attrs  => "$attr"
                             );
    if ( $mesg->code ) {
        logmsg("$name: error during LDAP search operation");
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count > 0 ) {
            if ( $mesg->count > 1 ) {
                logmsg("$name: found more than 1 LDAP entry for $uname (that's bad)");
                return(0);
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                $entry = $mesg->entry(0);

                # optype: modify
                if ( $optype eq "mod" ) {
                    debug("$name: modifying dn (replace attr val)=> " . $entry->dn);
                    if ( $entry->exists("$attr")) {
                        $result = $ldapref->modify($entry, replace=>{$attr => $val});
                        if ( $result->code ) {
                            logmsg("$name: Error during LDAP modify operation");
                            _ldapStatus($result);
                            return(0);
                        } else {
                            debug("$name: successfully modified dn");
                            return(1);
                        }
                    } else {
                        logmsg("$name: can't modify attribute that doesn't exist");
                        return(0);
                    }

                # optype: add
                } elsif ( $optype eq "add" ) {
                    debug("$name: modifying dn (add attr val) => " . $entry->dn);
                    $result = $ldapref->modify($entry, add=>{$attr => $val});
                    if ( $result->code ) {
                        logmsg("$name: Error during LDAP modify operation");
                        _ldapStatus($result);
                        return(0);
                    } else {
                        debug("$name: successfully modified dn");
                        return(1);
                    }
                } else {
                    logmsg("$name: invalid optype ($optype)");
                    return(0);
                }
            }
        } else {
            logmsg("$name: FAILED to find an LDAP entry for $uname");
            return(0);
        }
    }

}


###############################################################################
# NAME        : _deleteLdapUserAttr
# DESCRIPTION : 
# REQUIRES    : 
# RETURNS     : 
# NOTES       :
###############################################################################
sub _deleteLdapUserAttr {
    my ($ldapref, $uname, $attr, $val) = @_;
    my $name = "_deleteLdapUserAttr";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    _checkLdapObjRef($ldapref) or die();

    my $mesg;
    my $entry;
    my $basedn = "o=gsb,dc=uchicago,dc=edu";
    my $result;

    $mesg = $ldapref->search( base   => "$basedn",
                              filter => '(&(uid=' . $uname . '))',
                              attrs  => "$attr"
                             );

    if ( $mesg->code ) {
        logmsg("$name: error during LDAP search operation");
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count > 0 ) {
            if ( $mesg->count > 1 ) {
                logmsg( "$name: found more than 1 LDAP entry for $uname (that's bad)" );
                return(0);
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                $entry = $mesg->entry(0);
                logmsg("$name: modifying dn => " . $entry->dn);

                # if they passed us a value for the attribute, delete that one
                # otherwise delete the entire attribute
                if ( defined($val) ) {
                    logmsg("$name: delete valued attribute");
                    $result = $ldapref->modify($entry, delete=>{$attr => [$val]});
                } else {
                    logmsg("$name: delete entire attribute");
                    $result = $ldapref->modify($entry, delete=>{$attr => []});
                }
                if ( $result->code ) {
                    logmsg("$name: Error during LDAP modify operation");
                    _ldapStatus($result);
                    return(0);
                } else {
                    logmsg("$name: successfully modified dn");
                    return(1);
                }
            }
        } else {
            logmsg("$name: FAILED to find an LDAP entry for $uname");
            return(0);
        }
    }

}


###############################################################################
# NAME        : 
# DESCRIPTION : 
# REQUIRES    : 
# RETURNS     : 
# NOTES       :
###############################################################################
sub _getLdapUserMailAddress {
    my ($ldapref, $uname) = @_;
    my $name = "_getLdapUserMailAddress";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $valtype     = "single";
    my $ldapattr    = "mail";
    my $mailaddress = "";

    my $mailaddress = _getLdapUserAttrVal( $ldapref, $valtype, $uname, $ldapattr );
    return($mailaddress);

}


###############################################################################
# NAME        : 
# DESCRIPTION : 
# REQUIRES    : 
# RETURNS     : 
# NOTES       :
###############################################################################
sub _getLdapUserMailAlternates {
    my ($ldapref, $uname) = @_;
    my $name = "_getLdapUserMailAlternates";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $valtype    = "multi";
    my $ldapattr   = "mailAlternateAddress";
    my @alternates = "";

    my @alternates = _getLdapUserAttrVal( $ldapref, $valtype, $uname, $ldapattr );
    return(@alternates);

}


###############################################################################
# NAME        : _isValidLdapAccount
# DESCRIPTION : checks LDAP to see if it exists or not
# REQUIRES    : scalar(ref(ldap), scalar(login)
# RETURNS     : TRUE or FALSE
# NOTES       :
###############################################################################
sub _isValidLdapAccount {
    my ($ldapref, $uname) = @_;
    my $name = "_isValidLdapAccount";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $retval     = 0;
    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldapref) or die();

    debug("$name: searching for $uname in $basedn");
    my $mesg = $ldapref->search( base   => "$basedn",
                                 filter => '(&(uid=' . $uname . '))' );
    if ( $mesg->code ) {
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count == 0 ) {
            debug("$name: no entry for $uname in $basedn");
            return(0);
        } elsif ( $mesg->count == 1 ) {
            debug("$name: found 1 entry for $uname in $basedn");
            return(1);
        } elsif ( $mesg->count > 1 ) {
            logmsg( "$name: *** WARNING *** found more than 1 entry for $uname in $basedn" );
            return(0);
        }
    }
}


###############################################################################
# NAME        :
# DESCRIPTION :
# REQUIRES    :
# RETURNS     :
# NOTES       :
###############################################################################
sub _emailacctchk {

    my ($ldapref, $email, $type, $o ) = @_; 
    my $name = "_emailacctchk";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $dnb       = 'ou=' . "$o" . ',o=gsb,dc=uchicago,dc=edu';
    my $filterval = '(|(' . "$type" . '=' . "$email" . '@*))';
    logmsg("$name: search base->$dnb");
    logmsg("$name: filter->$filterval");

    my $mesg = $ldapref->search( base   => "$dnb",
                                 filter => "$filterval" );

    my $retval = 0;

    if ( $mesg->count > 0 ) {
        $retval = 1;
    }

    logmsg("$name: returning with val $retval");
    return $retval;
}


###############################################################################
# NAME        :
# DESCRIPTION :
# REQUIRES    :
# RETURNS     :
# NOTES       :
###############################################################################
sub _isValidMailAccount {
    my ($chkuname) = @_;
    my $name = "_validMailAccount";
    logmsg("$name: entering with args @_");

    my $retval = 0;

    debug("$name: calling sub _emailacctchk()");
    if ( _emailacctchk( $chkuname, 'mail', 'people' ) ) {
        logmsg("$name: email account $chkuname already in use for ou=people");
        $retval = 1;
    }

    debug("$name: calling sub _emailacctchk()");
    if ( _emailacctchk( $chkuname, 'mail', 'aliases' ) ) {
        logmsg("$name: email account $chkuname already in use for ou=aliases");
        $retval = 1;
    }

    logmsg("$name: returning with val $retval");
    return ($retval);
}


###############################################################################
# NAME        : _ldapStatus
# DESCRIPTION : Prints out an LDAP status code and text
# ARGUMENTS   : scalar(ref(ldapmessage))
# RETURN      : TRUE
# NOTES       : 
###############################################################################
sub _ldapStatus {
    my ($ref) = @_;
    my $name = "_ldapStatus()";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    logmsg("$name: LDAP code " . $ref->code . " (" . $ref->error . ")" );

    return(1);
}


###############################################################################
# NAME        : _checkLdapObjRef
# DESCRIPTION : Checks to see if a passed reference is a Net::LDAP object
# ARGUMENTS   : scalar(ref(net::ldap))
# RETURN      : TRUE or die()
# NOTES       : 
###############################################################################
sub _checkLdapObjRef {
    my ($ref) = @_;
    my $name = "_checkLdapObjRef()";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    if ( ! $ref->isa('Net::LDAP')) {
        logmsg("$name: Reference passed isn't a Net::LDAP object from filename $filename at line $line");
        return(0);
    } else {
        return(1);
    }
}


###############################################################################
# NAME        : _setRoutedUser
# DESCRIPTION : Change used from forwarded to routed
# ARGUMENTS   : scalar(ref(ldap))
#             : scalar(user)
# RETURN      : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _setRoutedUser {
    my ($ldap, $uid) = @_;

    my $exchserver  = "gsbex.uchicago.edu";
    my $accounttype = "";;

    # ldap attributes to remove
    my @removeattr = (  'vacationStartDate',
                        'vacationEndDate',
                        'mailAutoReplyTimeout',
                        'mailAutoReplyTextInternal;lang-en',
                        'mailAutoReplyText;lang-en',
                        'mailAutoReplySubject;lang-en',
                        'nswmExtendedUserPrefs',
                        'pabURI',
                        'mailDeliveryOption',
                        'mailForwardingAddress',
                     ); 
                     
    # Make sure it's a valid account
    logmsg("Checking to see if account is valid");
    if ( _isValidLdapAccount($ldap, $uid)) {
        logmsg("  - Successful");
    } else {
        logmsg("  - Failed");
    }   
    
    # Make sure it's a staff account
    logmsg("Checking account type");
    $accounttype = _getLdapUserAttrVal($ldap, 'single', $uid, 'gsbaccounttype');
    if ( $accounttype ne "staff" and $accounttype ne "cs" and $accounttype ne "coders" ) { 
        logmsg("  - Failed");
        exit(0); 
    } else {
        logmsg("  - Successful");
    }   
    
    # Make sure it's forwarded to exchange
    logmsg("Checking to see if account is forwarded to exchange");
    if ( _getLdapUserAttrVal($ldap, 'single', $uid, 'mailForwardingAddress') ne "$uid\@gsbexch.uchicago.edu" ) {
        logmsg("  - Failed");
    } else {
        logmsg("  - Successful");
    }                                                              
                                                                   
    foreach my $remove (@removeattr) {                             
        logmsg("Removing old attribute => $remove");               
        if ( _deleteLdapUserAttr($ldap, $uid, $remove) ) {         
            logmsg("  - Successful");                              
        } else {                                                   
            logmsg("  - Failed");                                  
        }                                                          
    }                                                              
                                                                   
    # Set mailHost to be the exchange cluster                      
    logmsg("Setting mailHost to $exchserver");                     
    if ( _addOrModifyLdapUserAttrVal($ldap, 'mod', $uid, 'mailHost', $exchserver) ) {
        logmsg("  - Successful");                                  
    } else {                                                       
        logmsg("  - Failed");                                      
    } 
}


###############################################################################
# NAME        : _getLdapUserPassword
# DESCRIPTION : Get a user's ldap password
# ARGUMENTS   : scalar(ref(ldap))
#             : scalar(uid)
# RETURN      : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _getLdapUserPassword {
    my ($ldap, $uid) = @_;
    my $name = "_getLdapUserPassword()";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $a;
    my $pw;
    $pw = _getLdapUserAttrVal($ldap, 'single', $uid, 'userPassword');
    ($a,$pw) = split('}', $pw);

    return($pw);
}


###############################################################################
# NAME        : _setLdapUserPassword
# DESCRIPTION : Change user password in LDAP
# ARGUMENTS   : scalar(ref(ldap))
#             : scalar(uid)
#             : scalar(password)
# RETURN      : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _setLdapUserPassword {
    my ($ldap, $uid, $password) = @_;
    my $name = "_setLdapUserPassword()";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    # password storage format in ldap
    # options from the cfg file are crypt or sha
    my $pwstorage = $CFG->param("ldap.pwstorage");
    debug("$name: pwstorage from config file => $pwstorage");

    my $newpass = "{$pwstorage}$password";

    if ( _addOrModifyLdapUserAttrVal($ldap, 'mod', $uid, 'userPassword', $newpass) ) {
        debug("$name: changed password for $uid");
        return(1);
    } else {
        logmsg("$name: FAILED to change password for $uid");
        return(0);
    }
}


###############################################################################
# NAME        : _lockLdapUserAccount
# DESCRIPTION : Lock a user's ldap account
# ARGUMENTS   : scalar(ref(ldap))
#             : scalar(uid)
# RETURN      : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _lockLdapUserAccount {
    my ($ldap, $uid) = @_;
    my $name = "_lockLdapUserAccount()";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    # get the current ldap password
    # this won't have the storage format {} part
    my $curpwd = _getLdapUserPassword($ldap, $uid);

    # lock the account by prepending a * to the password
    my $newpwd =  "*$curpwd";

    if ( substr($curpwd,0,1) eq '*' ) {
        logmsg("$name: the account for $uid is already locked");
        return(0);
    } else {

        # set the password in LDAP to the new one
        if ( _setLdapUserPassword($ldap, $uid, $newpwd) ) {
            debug("$name: changed LDAP password for $uid to $newpwd");
            return(1);
        } else {
            logmsg("$name: FAILED to change password");
            return(0);
        }
    }

}


###############################################################################
# NAME        : _unlockLdapUserAccount
# DESCRIPTION : Unlock a user's ldap account
# ARGUMENTS   : scalar(ref(ldap))
#             : scalar(uid)
# RETURN      : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _unlockLdapUserAccount {
    my ($ldap, $uid) = @_;
    my $name = "_unlockLdapUserAccount()";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    # get the current ldap password
    # this won't have the storage format {} part
    my $curpwd = _getLdapUserPassword($ldap, $uid);

    if ( substr($curpwd,0,1) ne '*' ) {
        logmsg("$name: the account for $uid isn't locked");
        return(0);
    } else {

        # remove the * from the beginning of the password
        my $newpwd = substr($curpwd, 1);

        # set the password in LDAP to the new one
        if ( _setLdapUserPassword($ldap, $uid, $newpwd) ) {
            debug("$name: changed LDAP password for $uid to $newpwd");
            return(1);
        } else {
            logmsg("$name: FAILED to change password");
            return(0);
        }
    }

}



###############################################################################
# NAME        : _createLdapAccount
# DESCRIPTION :
# REQUIRES    :
# RETURNS     :
# NOTES       :
###############################################################################
sub _createLdapAccount {
    my ( $ldap, 
         $uname, 
         $firstname, 
         $lastname, 
         $class, 
         $password, 
         $uidnumber, 
         $gidnumber,
         $homedir, 
         $shell ) = @_;
    my $name = "_createLdapAccount";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");
    
    my $dn;
    my $retval     = 0;
    my $result;

    $dn = "uid=$uname" . ',ou=people,o=gsb,dc=uchicago,dc=edu';
    logmsg("$name: setting DN: $dn");
    
    my $retval = 1;
    my $exchgw = $CFG->param("msexchange.gateway");
    debug("$name: found config setting for exchange of: $exchgw");


    ###
    ### Clean names up
    ###
    $firstname =~ s/\s//g;      # remove whitespace
    $lastname  =~ s/\s//g;      # remove whitespace
    $firstname =~ s/\'//g;      # remove single quotes
    $lastname  =~ s/\'//g;      # remove single quotes
    
    ###
    ### Staff account
    ###
    ### mail = first.last@chicagogsb.edu
    ### mailAlternateAddress = first.last@gsb.uchicago.edu
    ### mailAlternateAddress = username@gsb.uchicago.edu
    ###
    
    ### we're fed "highclass" by the caller..
    if ( $class eq "s" or $class eq "f" or $class eq "ts" or $class eq "c" or $class eq "r") {
    
    	logmsg("$name: creating ldap entry for staff account");
    	
    	$result = $ldap->add( "$dn",
    	                  attr => ['cn'                       => qq($firstname) . " " . qq($lastname),
    	                           'givenname'                => qq($firstname),
    	                           'gsbaccountstatus'         => 'A',
    	                           'gsbaccounttype'           => $GCH_LDAPCLASS{$class},
    	                           'sn'                       => qq($lastname),
    	                           'mail'                     => "$firstname.$lastname\@chicagobooth.edu",
    	                           'mailAlternateAddress'     => "$firstname.$lastname\@chicagogsb.edu",
    	                           'mailAlternateAddress'     => "$firstname.$lastname\@gsb.uchicago.edu",
    	                           'mailAlternateAddress'     => "$uname\@chicagobooth.edu",
    	                           'mailAlternateAddress'     => "$uname\@chicagogsb.edu",
    	                           'mailAlternateAddress'     => "$uname\@gsb.uchicago.edu",
    	                           'uid'                      => $uname,
    	                           'userPassword'             => '{crypt}' . "$password",
    	                           'mailUserStatus'           => 'active',
    	                           'inetUserStatus'           => 'active',
    	                           'mailDeliveryOption'       => 'forward',
    	                           'mailForwardingAddress'    => "$uname\@$exchgw",
    	                           'mailHost'                 => $GCH_MAILHOST{$class},
    	                           'mailMessageStore'         => $GCH_MAILSTORE{$class},
    	                           'mailAllowedServiceAccess' => $GCH_MAILACCESS{$class},
    	                           'mailQuota'                => $GCH_MAILQUOTA{$class},
                                   'uidNumber'                => $uidnumber,
                                   'gidNumber'                => $gidnumber,
                                   'homeDirectory'            => $homedir,
                                   'loginShell'               => $shell,
    	                           'objectclass'              => [
    	                               'top',
    	                               'person',
    	                               'organizationalPerson',
    	                               'inetOrgPerson',
    	                               'gsbperson',
    	                               'inetMailUser',
    	                               'ipuser',
    	                               'inetuser',
    	                               'inetlocalmailrecipient',
    	                               'nsManagedPerson',
                                       'posixAccount',
                                       'shadowAccount'
    	                               ],
    	                      ]
    	                      );

    } elsif ( $class eq "m" or $class eq "tm" or $class eq "n" or $class eq "p" or $class eq "l" or $class eq "a") {

            if ( $CFG->param("msexchange.forward-students") ) {
    	        logmsg("$name: set to forward users (class => m,tm,n,p,a) to exchange");
    	        $result = $ldap->add( "$dn",
		    	                  attr => ['cn'                       => qq($firstname) . " " . qq($lastname),
		    	                           'givenname'                => qq($firstname),
		    	                           'gsbaccountstatus'         => 'A',
		    	                           'gsbaccounttype'           => $GCH_LDAPCLASS{$class},
		    	                           'sn'                       => qq($lastname),
		    	                           'mail'                     => "$uname\@chicagobooth.edu",
		    	                           'mailAlternateAddress'     => "$uname\@chicagogsb.edu",
		    	                           'mailAlternateAddress'     => "$uname\@gsb.uchicago.edu",
		    	                           'uid'                      => $uname,
		    	                           'userPassword'             => '{crypt}' . "$password",
		    	                           'mailUserStatus'           => 'active',
		    	                           'inetUserStatus'           => 'active',
		    	                           'mailDeliveryOption'       => 'forward',
		    	                           'mailForwardingAddress'    => "$uname\@$exchgw",
		    	                           'mailHost'                 => $GCH_MAILHOST{$class},
		    	                           'mailMessageStore'         => $GCH_MAILSTORE{$class},
		    	                           'mailAllowedServiceAccess' => $GCH_MAILACCESS{$class},
		    	                           'mailQuota'                => $GCH_MAILQUOTA{$class},
                                           'uidNumber'                => $uidnumber,
                                           'gidNumber'                => $gidnumber,
                                           'homeDirectory'            => $homedir,
                                           'loginShell'               => $shell,
		    	                           'objectclass'              => [
		    	                               'top',
		    	                               'person',
		    	                               'organizationalPerson',
		    	                               'inetOrgPerson',
		    	                               'gsbperson',
		    	                               'inetMailUser',
		    	                               'ipuser',
		    	                               'inetuser',
		    	                               'inetlocalmailrecipient',
		    	                               'nsManagedPerson',
                                               'posixAccount',
                                               'shadowAccount'
		    	                               ],
		    	                      ]
    	                      );
    	    } else {
    	    
                # Non exchange forwarded account
    	    	$result = $ldap->add( "$dn",
	    		    	         attr => ['cn'                       => qq($firstname) . " " . qq($lastname),
	    		    	                  'givenname'                => qq($firstname),
	    		    	                  'gsbaccountstatus'         => 'A',
	    		    	                  'gsbaccounttype'           => $GCH_LDAPCLASS{$class},
	    		    	                  'sn'                       => qq($lastname),
	    		    	                  'mail'                     => "$uname\@chicagobooth.edu",
	    		    	                  'mailAlternateAddress'     => "$uname\@chicagogsb.edu",
	    		    	                  'mailAlternateAddress'     => "$uname\@gsb.uchicago.edu",
	    		    	                  'uid'                      => $uname,
	    		    	                  'userPassword'             => '{crypt}' . "$password",
	    		    	                  'mailUserStatus'           => 'active',
	    		    	                  'inetUserStatus'           => 'active',
	    		    	                  'mailDeliveryOption'       => 'mailbox',
	    		    	                  'mailHost'                 => $GCH_MAILHOST{$class},
	    		    	                  'mailMessageStore'         => $GCH_MAILSTORE{$class},
	    		    	                  'mailAllowedServiceAccess' => '+ALL:ALL',
	    		    	                  'mailQuota'                => $GCH_MAILQUOTA{$class},
                                          'uidNumber'                => $uidnumber,
                                          'gidNumber'                => $gidnumber,
                                          'homeDirectory'            => $homedir,
                                          'loginShell'               => $shell,
	    		    	                  'objectclass'              => [
	    		    	                      'top',
	    		    	                      'person',
	    		    	                      'organizationalPerson',
	    		    	                      'inetOrgPerson',
	    		    	                      'gsbperson',
	    		    	                      'inetMailUser',
	    		    	                      'ipuser',
	    		    	                      'inetuser',
	    		    	                      'inetlocalmailrecipient',
	    		    	                      'nsManagedPerson',
                                              'posixAccount',
                                              'shadowAccount'
	    		    	                      ],
	    		    	             ]
    	                      );
    	    }
    
    } else {
    
        # everyone else
    	$result = $ldap->add( "$dn",
    	    		    	         attr => ['cn'                       => qq($firstname) . " " . qq($lastname),
    	    		    	                  'givenname'                => qq($firstname),
    	    		    	                  'gsbaccountstatus'         => 'A',
    	    		    	                  'gsbaccounttype'           => $GCH_LDAPCLASS{$class},
    	    		    	                  'sn'                       => qq($lastname),
    	    		    	                  #'mail'                     => "$uname\@chicagobooth.edu",
    	    		    	                  #'mailAlternateAddress'     => "$uname\@chicagogsb.edu",
    	    		    	                  #'mailAlternateAddress'     => "$uname\@gsb.uchicago.edu",
    	    		    	                  'uid'                      => $uname,
    	    		    	                  'userPassword'             => '{crypt}' . "$password",
    	    		    	                  'mailUserStatus'           => 'active',
    	    		    	                  'inetUserStatus'           => 'active',
    	    		    	                  #'mailDeliveryOption'       => 'mailbox',
    	    		    	                  #'mailHost'                 => $GCH_MAILHOST{$class},
    	    		    	                  #'mailMessageStore'         => $GCH_MAILSTORE{$class},
    	    		    	                  #'mailAllowedServiceAccess' => $GCH_MAILACCESS{$class},
    	    		    	                  #'mailQuota'                => $GCH_MAILQUOTA{$class},
                                              'uidNumber'                => $uidnumber,
                                              'gidNumber'                => $gidnumber,
                                              'homeDirectory'            => $homedir,
                                              'loginShell'               => $shell,
    	    		    	                  'objectclass'              => [
    	    		    	                      'top',
    	    		    	                      'person',
    	    		    	                      'organizationalPerson',
    	    		    	                      'inetOrgPerson',
    	    		    	                      'gsbperson',
    	    		    	                      'inetMailUser',
    	    		    	                      'ipuser',
    	    		    	                      'inetuser',
    	    		    	                      'inetlocalmailrecipient',
    	    		    	                      'nsManagedPerson',
                                                  'posixAccount',
                                                  'shadowAccount'
    	    		    	                      ],
    	    		    	             ]
    	                      );
    
    }

    if ( $result->code ) {
        logmsg("$name: FAILED to add LDAP account for user $uname");
        logmsg( "$name: LDAP error is: " . $result->error );
        $retval = 0;
    } else {
        logmsg("$name: added LDAP account to $host:$port for user $uname");
    }

    logmsg("$name: returning with val $retval");
    return $retval;

}


###############################################################################
# NAME        : _deleteLdapAccount
# DESCRIPTION :
# REQUIRES    :
# RETURNS     :
# NOTES       :
###############################################################################
sub _deleteLdapAccount {
    my ( $ldap, $uname ) = @_;
    my $name = "_deleteLdapAccount";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $retval = 1;

    # The basic remove process
    #    set inetUserStatus to deleted
    #    set mailUserStatus to deleted
    #    set gsbaccountstatus to D
    #    -> msuserpurge handles the purge of the
    #       msgsvr mailbox, pab and ldap data

    #
    if ( _addOrModifyLdapUserAttrVal($ldap, 'mod', $uname, 'inetUserStatus', 'deleted') ) {
        debug("$name: changed inetUserStatus for $uname to deleted");
    } else {
        logmsg("$name: FAILED to change inetUserStatus for $uname");
        $retval = 0;
    }

    #
    if ( _addOrModifyLdapUserAttrVal($ldap, 'mod', $uname, 'mailUserStatus', 'deleted') ) {
        debug("$name: changed mailUserStatus for $uname to deleted");
    } else {
        logmsg("$name: FAILED to change mailUserStatus for $uname");
        $retval = 0;
    }

    #
    if ( _addOrModifyLdapUserAttrVal($ldap, 'mod', $uname, 'gsbaccountstatus', 'D') ) {
        debug("$name: changed gsbaccountstatus for $uname to D");
    } else {
        logmsg("$name: FAILED to change gsbaccountstatus for $uname");
        $retval = 0;
    }

    return($retval);
}


###############################################################################
# NAME        : _getRemovedLdapAccounts
# DESCRIPTION :
# REQUIRES    :
# RETURNS     :
# NOTES       :
###############################################################################
sub _getRemovedLdapAccounts {
    my ( $ldapref ) = @_;
    my $name = "_getRemovedLdapAccounts";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my @removed;
    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldapref) or die();

    debug("$name: searching for removed accounts in $basedn");
    my $mesg = $ldapref->search( base   => "$basedn",
                                 filter => '(&(inetUserStatus=deleted)(mailUserStatus=removed))' );
    if ( $mesg->code ) {
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count == 0 ) {
            debug("$name: no accounts flagged as deleted/removed");
            return(@removed);
        } else {
            logmsg("$name: found " .  $mesg->count . " accounts to be purged");
            my $max = $mesg->count;
            for ( $i = 0; $i < $max; $i++ ) {
                my $entry = $mesg->entry($i);
                logmsg("$name: flagged as deleted/removed " . $entry->get_value('uid'));
                push(@removed, $entry->get_value('uid'));
            }
            return(@removed);
        }
    }
}


###############################################################################
# NAME        : _deleteUserPab
# DESCRIPTION :
# REQUIRES    :
# RETURNS     :
# NOTES       :
###############################################################################
sub _deleteUserPab {
    my ( $ldapref, $uname ) = @_;
    my $name = "_deleteUserPab";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    _checkLdapObjRef($ldapref) or die();

    my $basedn = "ou=$uname,ou=people,o=gsb,dc=uchicago,dc=edu,o=pab";

    # Start this search from the top level of the LDAP tree
    logmsg("Starting search for pab basedn $basedn");
    $mesg = $ldapref->search( base   => "$basedn",
                              filter => "un=*"
                             );      

    if ( $mesg->code ) {
        if ( $mesg->code == 32 ) {
            logmsg("$name: no pab in ldap for user $uname");
            return(1);
        } else {
            logmsg("$name: received funky message code from ldap search: " . $mesg->code);
            return(0);
        }
    } else {
        if ( $mesg->count > 0 ) { 
            logmsg("$name: found " . $mesg->count . " entries in pab for $uname" );
            my $max = $mesg->count;
            for ( $i = 0; $i < $max; $i++ ) {
                my $entry = $mesg->entry($i);
                $ldapref->delete($entry);
                logmsg("$name: delete pab entry: " . $entry->dn);
            }

            logmsg("$name: delete user pab: $basedn");
            $ldapref->delete($basedn);
        } else{
            logmsg("FAILED to find an LDAP entry for $uname (huh?)");
            return(0);
        } 
    }
}


###############################################################################
# NAME        : _purgeLdapAccount
# DESCRIPTION :
# REQUIRES    :
# RETURNS     :
# NOTES       :
###############################################################################
sub _purgeLdapAccount {
    my ( $ldapref, $uname ) = @_;
    my $name = "_purgeLdapAccount";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldapref) or die();

    debug("$name: searching uid: $uname");
    my $mesg = $ldapref->search( base   => "$basedn",
                                 filter => "uid=$uname" );

    if ( $mesg->code ) {
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count > 0 ) { 
            if ( $mesg->count > 1 ) { 
                logmsg( "$name: found more than 1 LDAP entry for $uname (that's bad)" );
                return(0);
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                $entry = $mesg->entry(0);

                my $dn = $entry->dn;

                logmsg("$name: going to delete dn: $dn");
                if ( $ldapref->delete($dn) ) {
                    logmsg("$name: deleted dn: $dn");
                    return(1);
                } else {
                    logmsg("$name: FAILED to delete dn: $dn");
                    return(0);
                }
            }

        } else {
            logmsg("$name: FAILED to find an LDAP entry for $uname");
            return(0);
        }       
    }

}


###############################################################################
# NAME        : _forwardAccountToExchange
# DESCRIPTION :
# REQUIRES    :
# RETURNS     :
# NOTES       :
###############################################################################
sub _forwardAccountToExchange {
    my ( $ldapref, $uname ) = @_;
    my $name = "_forwardAccountToExchange";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");
    
}


###############################################################################
# NAME        : _isValidOU
# DESCRIPTION : Checks to see if an OU exists under BASEDN
# REQUIRES    : scalar(ldapref), scalar(ou)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _isValidOU {
    my ( $ldapref, $ou ) = @_;
    my $name = "_isValidOU";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldapref) or die();

    my $mesg = $ldapref->search( base   => "$basedn",
                                 filter => "(&(ou=$ou)(objectClass=organizationalUnit))");
    if ( $mesg->code ) { 
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count == 0 ) { 
            debug("$name: no OU present");
            return(0);
        } else {
            debug("$name: found " .  $mesg->count . " OU's");
            my $entry = $mesg->entry(0);
            return(1);
        }
    }
}


###############################################################################
# NAME        : _isNetgroup
# DESCRIPTION : Checks to see if a given netgroup exists 
# REQUIRES    : scalar(ldapref), scalar(netgroup)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _isNetgroup {
    my ( $ldapref, $netgroup ) = @_;
    my $name = "_isNetgroup";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldapref) or die();

    my $filter = "(&(objectClass=nisNetgroup)(cn=$netgroup))";

    debug("$name: filter => $filter");

    my $mesg = $ldapref->search( base   => "$basedn",
                                 filter => "$filter");
    if ( $mesg->code ) { 
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count == 0 ) { 
            debug("$name: entry not found");
            return(0);
        } else {
            debug("$name: found " .  $mesg->count . " entries");
            return(1);
        }
    }
}


###############################################################################
# NAME        : _createNetgroup
# DESCRIPTION : Create a netgroup 
# REQUIRES    : scalar(ldapref), scalar(netgroup)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _createNetgroup {
    my ( $ldap, $netgroup ) = @_;
    my $name = "_createNetgroup";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldap) or die();

    my $dn = "cn=$netgroup,ou=netgroup,$basedn";
    debug("$name: dn to create is => $dn");

    # add the entry
    $result = $ldap->add( "$dn",
                                attr => ['cn'          => $netgroup,
    	    		    	             'objectclass' => [ 'top', 'nisNetgroup' ],
    	    		    	            ]
    	                      );

    if ( $result->code ) {
        logmsg("$name: FAILED to add LDAP netgroup entry for $netgroup");
        logmsg( "$name: LDAP error is: " . $result->error );
        return(0);
    } else {
        debug("$name: added LDAP netgroup entry for $netgroup");
        return(1);
    }
}


###############################################################################
# NAME        : _addUserToNetgroup
# DESCRIPTION : Add a user to a netgroup
# REQUIRES    : scalar(ldapref), scalar(user), scalar(netgroup)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _addUserToNetgroup {
    my ( $ldapref, $user, $netgroup ) = @_;
    my $name = "_addUserToNetgroup";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";
    my $mesg;
    my $filter = "(&(cn=$netgroup)(objectClass=nisNetgroup))";

    my $attr = "nisNetgroupTriple";
    my $val = "(,$user,)";

    _checkLdapObjRef($ldapref) or die();

    $mesg = $ldapref->search( base   => "$basedn",
                              filter => $filter
                             );
    if ( $mesg->code ) {
        logmsg("$name: error during LDAP search operation");
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count > 0 ) {
            if ( $mesg->count > 1 ) {
                logmsg("$name: found more than 1 LDAP entry for $group (that's bad)");
                return(0);
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                $entry = $mesg->entry(0);

                debug("$name: modifying dn (add attr val) => " . $entry->dn);
                $result = $ldapref->modify($entry, add=>{$attr => $val});
                if ( $result->code ) {
                    logmsg("$name: Error during LDAP modify operation");
                    _ldapStatus($result);
                    return(0);
                } else {
                    debug("$name: successfully modified dn");
                    return(1);
                }
            }
        } else {
            logmsg("$name: FAILED to find an LDAP entry for netgroup: $netgroup");
            return(0);
        }
    }
}


###############################################################################
# NAME        : _deleteNetgroup
# DESCRIPTION : Delete a netgroup 
# REQUIRES    : scalar(ldapref), scalar(netgroup)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _deleteNetgroup {
    my ( $ldap, $netgroup ) = @_;
    my $name = "_deleteNetgroup";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldap) or die();

    my $dn = "cn=$netgroup,ou=netgroup,$basedn";
    debug("$name: dn to delete is => $dn");

    if ( _isNetgroup($ldap, $netgroup) ) {
        $ldap->delete($dn);
        debug("$name: deleted ldap netgroup => $netgroup");
        return(1);
    } else {
        logmsg("$name: FAILED to delete netgroup because it doesn't exist");
        return(0);
    }
}


###############################################################################
# NAME        : _createAutoHomeEntry
# DESCRIPTION : Create a user's auto_home entry
# REQUIRES    : scalar(ldapref)
#             : scalar(username)
#             : scalar(homesrv)
#             : scalar(homedir)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _createAutoHomeEntry {
    my ( $ldap, $username, $homesrv, $homedir ) = @_;
    my $name = "_createAutoHomeEntry";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldap) or die();

    my $dn = "automountKey=$username,automountMapName=auto_home,$basedn";
    my $automountKey = "$username";
    my $automountInformation = "$homesrv:$homedir/&";

    logmsg("$name: Create DN             => $dn");
    logmsg("$name: automountKey          => $automountKey");
    logmsg("$name: automount Information => $automountInformation");

    ### Check to see if the entry exists, if so, delete it.

    # add the entry
    $result = $ldap->add( "$dn",
                                attr => ['automountKey' => $automountKey,
                                         'automountInformation' => "$automountInformation",
    	    		    	             'objectclass' => [ 'top', 'automount' ],
    	    		    	            ]
    	                      );

    if ( $result->code ) {
        logmsg("$name: FAILED to add LDAP entry for $username");
        logmsg( "$name: LDAP error is: " . $result->error );
        return(0);
    } else {
        debug("$name: added LDAP entry for $username");
        return(1);
    }
}


###############################################################################
# NAME        : _isPOSIXgroup
# DESCRIPTION : Checks to see if a given POSIX group exists 
# REQUIRES    : scalar(ldapref), scalar(netgroup)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _isPOSIXGroup {
    my ( $ldapref, $group ) = @_;
    my $name = "_isPOSIXgroup";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldapref) or die();

    my $filter = "(&(objectClass=posixGroup)(cn=$group))";

    debug("$name: filter => $filter");

    my $mesg = $ldapref->search( base   => "$basedn",
                                 filter => "$filter");
    if ( $mesg->code ) { 
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count == 0 ) { 
            debug("$name: entry not found");
            return(0);
        } else {
            debug("$name: found " .  $mesg->count . " entries");
            return(1);
        }
    }
}


###############################################################################
# NAME        : _deletePOSIXGroup
# DESCRIPTION : Delete an LDAP POSIX Group
# REQUIRES    : scalar(ldapref), scalar(group)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _deletePOSIXGroup {
    my ( $ldap, $group ) = @_;
    my $name = "_deletePOSIXGroup";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldap) or die();

    my $dn = "cn=$group,ou=group,$basedn";
    debug("$name: dn to delete is => $dn");

    if ( _isPOSIXGroup($ldap, $group) ) {
        $ldap->delete($dn);
        debug("$name: deleted ldap group => $group");
        return(1);
    } else {
        logmsg("$name: FAILED to delete group because it doesn't exist");
        return(0);
    }
}


###############################################################################
# NAME        : _createPOSIXGroup
# DESCRIPTION : Create a POSIX group in LDAP
# REQUIRES    : scalar(ldapref), scalar(group)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _createPOSIXGroup {
    my ( $ldap, $group, $gidnumber ) = @_;
    my $name = "_createPOSIXGroup";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";

    _checkLdapObjRef($ldap) or die();

    my $dn = "cn=$group,ou=group,$basedn";
    debug("$name: dn to create is => $dn");

    # add the entry
    $result = $ldap->add( "$dn",
                                attr => ['cn'           => $group,
                                         'userPassword' => '{crypt}*',
                                         'gidNumber'    => $gidnumber,
    	    		    	             'objectclass'  => [ 'top', 'posixGroup' ],
    	    		    	            ]
    	                      );

    if ( $result->code ) {
        logmsg("$name: FAILED to add LDAP group entry for $group");
        logmsg( "$name: LDAP error is: " . $result->error );
        return(0);
    } else {
        debug("$name: added LDAP group entry for $group");
        return(1);
    }
}


###############################################################################
# NAME        : _addUserToPOSIXGroup
# DESCRIPTION : Add a user to an LDAP POSIX Group
# REQUIRES    : scalar(ldapref), scalar(user), scalar(group)
# RETURNS     : TRUE or FALSE
# NOTES       : None
###############################################################################
sub _addUserToPOSIXGroup {
    my ( $ldapref, $user, $group ) = @_;
    my $name = "_addUserToPOSIXGroup";
    my ( $package, $filename, $line ) = caller;
    debug("$name: entering with args @_");
    debug("$name: called from package->$package, filename->$filename, line->$line");

    my $basedn     = "o=gsb,dc=uchicago,dc=edu";
    my $mesg;
    my $filter = "(&(cn=$group)(objectClass=posixGroup))";

    my $attr = "memberUid";
    my $val = "$user";

    _checkLdapObjRef($ldapref) or die();

    $mesg = $ldapref->search( base   => "$basedn",
                              filter => $filter
                             );
    if ( $mesg->code ) {
        logmsg("$name: error during LDAP search operation");
        _ldapStatus($mesg);
        return(0);
    } else {
        if ( $mesg->count > 0 ) {
            if ( $mesg->count > 1 ) {
                logmsg("$name: found more than 1 LDAP entry for $group (that's bad)");
                return(0);
            } else {
                debug( "$name: found " . $mesg->count . " entries" );
                $entry = $mesg->entry(0);

                debug("$name: modifying dn (add attr val) => " . $entry->dn);
                $result = $ldapref->modify($entry, add=>{$attr => $val});
                if ( $result->code ) {
                    logmsg("$name: Error during LDAP modify operation");
                    _ldapStatus($result);
                    return(0);
                } else {
                    debug("$name: successfully modified dn");
                    return(1);
                }
            }
        } else {
            logmsg("$name: FAILED to find an LDAP entry for group: $group");
            return(0);
        }
    }
}

1;
